////////////////////////////////////////////////////////////////////////////////
// Filename: Blur.fx
// HLSL Shader File to Render Textures to screen.
////////////////////////////////////////////////////////////////////////////////

/////////////
// GLOBALS //
/////////////
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
Texture2D shaderTexture;

// Horizontal blur processing
float screenWidth;
// Vertical blur processing
float screenHeight;

///////////////////
// SAMPLE STATES //
///////////////////
SamplerState SampleType
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 htex		: TEXCOORD0;
	float2 vtex		: TEXCOORD10;
};

struct PixelInputType
{
    float4 position	: SV_POSITION;

	// Horizontal tex coords
    float2 htex		: TEXCOORD0;
	float2 hCoord1	: TEXCOORD1;
	float2 hCoord2	: TEXCOORD2;
	float2 hCoord3	: TEXCOORD3;
	float2 hCoord4	: TEXCOORD4;
	float2 hCoord5	: TEXCOORD5;
	float2 hCoord6	: TEXCOORD6;
	float2 hCoord7	: TEXCOORD7;
	float2 hCoord8	: TEXCOORD8;
	float2 hCoord9	: TEXCOORD9;

	// Vertical tex coords
	float2 vtex		: TEXCOORD10;
	float2 vCoord1	: TEXCOORD11;
	float2 vCoord2	: TEXCOORD12;
	float2 vCoord3	: TEXCOORD13;
	float2 vCoord4	: TEXCOORD14;
	float2 vCoord5	: TEXCOORD15;
	float2 vCoord6	: TEXCOORD16;
	float2 vCoord7	: TEXCOORD17;
	float2 vCoord8	: TEXCOORD18;
	float2 vCoord9	: TEXCOORD19;


};

////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType BlurVertexShader(VertexInputType input)
{
    PixelInputType output;
	float texelSize;
    
	// Change the position vector to be 4 units for proper matrix calculations.
    input.position.w = 1.0f;

	// Calculate the position of the vertex against the world, view, and projection matrices.
    output.position = mul(input.position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
    
	// Store the texture coordinates for horizontal.
    output.htex = input.htex;

	// Determine Texel size to get UV coord of neighbouring pixels
	texelSize = 1.0f / screenWidth;

	// Set UV coords for center pixel and 4 either side - Left one pixel is texelSize * -1.0f etc.
	output.hCoord1 = input.htex + float2(texelSize * -4.0f, 0.0f); // Left 4 pixels
	output.hCoord2 = input.htex + float2(texelSize * -3.0f, 0.0f);
	output.hCoord3 = input.htex + float2(texelSize * -2.0f, 0.0f);
	output.hCoord4 = input.htex + float2(texelSize * -1.0f, 0.0f);
	output.hCoord5 = input.htex + float2(texelSize *  0.0f, 0.0f); // Center
	output.hCoord6 = input.htex + float2(texelSize *  1.0f, 0.0f);
	output.hCoord7 = input.htex + float2(texelSize *  2.0f, 0.0f);
	output.hCoord8 = input.htex + float2(texelSize *  3.0f, 0.0f);
	output.hCoord9 = input.htex + float2(texelSize *  4.0f, 0.0f); // Right 4 pixels
    
	// store tex coords for vert
	output.vtex = input.vtex;

	// same as texelsize above but for height not width
	texelSize = 1.0f / screenHeight;

	// Set UV coords for center pixel and 4 either side - Down one pixel is texelSize * -1.0f etc.
	output.vCoord1 = input.vtex + float2(texelSize * -4.0f, 0.0f); // Down 4 pixels
	output.vCoord2 = input.vtex + float2(texelSize * -3.0f, 0.0f);
	output.vCoord3 = input.vtex + float2(texelSize * -2.0f, 0.0f);
	output.vCoord4 = input.vtex + float2(texelSize * -1.0f, 0.0f);
	output.vCoord5 = input.vtex + float2(texelSize *  0.0f, 0.0f); // Center
	output.vCoord6 = input.vtex + float2(texelSize *  1.0f, 0.0f);
	output.vCoord7 = input.vtex + float2(texelSize *  2.0f, 0.0f);
	output.vCoord8 = input.vtex + float2(texelSize *  3.0f, 0.0f);
	output.vCoord9 = input.vtex + float2(texelSize *  4.0f, 0.0f); // Up 4 pixels

	return output;
}


////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 BlurPixelShader(PixelInputType input) : SV_Target
{
	float weight0, weight1, weight2, weight3, weight4;
	float normalisation;
	float4 colour;

	// Affect weight of nearest neighbouring pixels
	weight0 = 1.0f;
	weight1 = 0.9f;
	weight2 = 0.55f;
	weight3 = 0.18f;
	weight4 = 0.1f;

	// Normalise the weight to create smoothness
	normalisation = (weight0 + 0.2f * (weight1 + weight2 + weight3 + weight4));

	weight0 = weight0/normalisation;
	weight1 = weight1/normalisation;
	weight2 = weight2/normalisation;
	weight3 = weight3/normalisation;
	weight4 = weight4/normalisation;

	// Blur pixel by setting colour to black, add center pixel and eight neighbours to final colour based on weight
	colour = float4(0.0f, 0.0f, 0.0f, 0.0f);

	// Horizontal
	colour += shaderTexture.Sample(SampleType, input.hCoord1) * weight4;
	colour += shaderTexture.Sample(SampleType, input.hCoord2) * weight3;
	colour += shaderTexture.Sample(SampleType, input.hCoord3) * weight2;
	colour += shaderTexture.Sample(SampleType, input.hCoord4) * weight1;
	colour += shaderTexture.Sample(SampleType, input.hCoord5) * weight0;
	colour += shaderTexture.Sample(SampleType, input.hCoord6) * weight1;
	colour += shaderTexture.Sample(SampleType, input.hCoord7) * weight2;
	colour += shaderTexture.Sample(SampleType, input.hCoord8) * weight3;
	colour += shaderTexture.Sample(SampleType, input.hCoord9) * weight4;

	// Vertical
	colour += shaderTexture.Sample(SampleType, input.vCoord1) * weight4;
	colour += shaderTexture.Sample(SampleType, input.vCoord2) * weight3;
	colour += shaderTexture.Sample(SampleType, input.vCoord3) * weight2;
	colour += shaderTexture.Sample(SampleType, input.vCoord4) * weight1;
	colour += shaderTexture.Sample(SampleType, input.vCoord5) * weight0;
	colour += shaderTexture.Sample(SampleType, input.vCoord6) * weight1;
	colour += shaderTexture.Sample(SampleType, input.vCoord7) * weight2;
	colour += shaderTexture.Sample(SampleType, input.vCoord8) * weight3;
	colour += shaderTexture.Sample(SampleType, input.vCoord9) * weight4;

	// set alpha
	colour.a = 1.0f;

	return colour;
}


////////////////////////////////////////////////////////////////////////////////
// Technique
////////////////////////////////////////////////////////////////////////////////
technique10 BlurTechnique
{
    pass pass0
    {
        SetVertexShader(CompileShader(vs_4_0, BlurVertexShader()));
        SetPixelShader(CompileShader(ps_4_0, BlurPixelShader()));
		SetGeometryShader(NULL);
    }
}